﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Animations;
using Silvermoon.ComponentModel;
using System.Drawing;
using Silvermoon.ComponentModel.Beta;

namespace Silvermoon.Controls.Beta
{
    public class ListBoxAnimator
    {
        #region classes

        abstract class ItemAnimation
        {
            protected ListViewItemCollection Items { get { return Animator.items; } }

            public ItemBase Item;
            public int Index;

            protected IntAnimation Animation;
            public ListBoxAnimator Animator { get; private set; }

            public ItemAnimation(ListBoxAnimator animator, int index)
                : base()
            {
                this.Animator = animator;
                this.Index = index;

                Animation = new IntAnimation(600, 0, 255, 0);
                Animation.EasingFunction = SineEase.InOut;
                Animation.Progress += new EventHandler<AnimEventArgs<int>>(OnAnimationProgress);
                Animation.Completed += new EventHandler<AnimEventArgs<int>>(OnAnimationComplete);

                Item = Items[index];
            }

            protected void SetItemAnimationState(ItemAnimationType type, int value)
            {
                Rectangle bounds = Item.Bounds;
                Item.SetAnimationState(ItemAnimationType.Insert, value, 255, ref bounds);
                Item.Bounds = bounds;
            }



            void OnAnimationComplete(object sender, AnimEventArgs<int> e)
            {
                OnComplete();
                Item.AnimatedOffset = 0;
                Animator.itemAnimations.Remove(this);
            }

            void OnAnimationProgress(object sender, AnimEventArgs<int> e)
            {
                OnProgress(e.Value);
                this.Animator.listBox.ItemHeightChanged(Item);
//                Items.ItemHeightChanged(Item);
//                Items.UpdateListBox();
            }

            public void Stop()
            {
                Animation.Abort();
            }

            public void Invoke()
            {
                OnProgress(0);
                Animation.InvokeAsync();
            }

            protected abstract void OnComplete();
            protected abstract void OnProgress(int value);

        }

        class RemoveItemAnimation : ItemAnimation
        {

            public RemoveItemAnimation(ListBoxAnimator animator, int index)
                : base(animator, index)
            { }

            protected override void OnComplete()
            {
                Items.RemoveAt(Index);
            }

            protected override void OnProgress(int value)
            {
                Item.AnimatedOffset = -Item.Height * value / 255;

                SetItemAnimationState(ItemAnimationType.Insert, 255 - value);
            }
        }

        class InsertItemAnimation : ItemAnimation
        {
            public InsertItemAnimation(ListBoxAnimator animator, int index)
                : base(animator, index)
            { }

            protected override void OnComplete()
            {
            }

            protected override void OnProgress(int value)
            {
                value = 255 - value;
                Item.AnimatedOffset = -Item.Height * value / 255;
                SetItemAnimationState(ItemAnimationType.Insert, 255 - value);
            }
        }
        #endregion

        private ListBoxBase listBox;
        private ListViewItemCollection items;
        private List<ItemAnimation> itemAnimations = new List<ItemAnimation>();


        public ListBoxAnimator(ListBoxBase listBox)
            : base()
        {
            this.listBox = listBox;
            this.items = listBox.Items;
            this.items.ListChanged += OnListChanged;
        }

        private void OnListChanged(object sender, ItemListChangedEventArgs e)
        {
            switch (e.ListChangedType)
            {
                case ItemListChangedType.ItemAdded:
                    UpdateForInsert(e.NewIndex);
                    break;

                case ItemListChangedType.ItemDeleted:
                    UpdateForRemove(e.OldIndex);
                    break;

                case ItemListChangedType.Reset:
                    if (items.Count == 0)
                    {
                        Clear();
                    }
                    break;
            }
        }

        private void Clear()
        {
            if (itemAnimations.Count > 0)
            {
                foreach (var a in itemAnimations)
                {
                    a.Stop();
                }
                itemAnimations.Clear();
            }
        }

        private void UpdateForRemove(int index)
        {
            foreach (var a in itemAnimations)
            {
                if (a.Index > index) a.Index--;
            }
        }

        private void UpdateForInsert(int index)
        {
            foreach (var a in itemAnimations)
            {
                if (a.Index <= index) a.Index++;
            }
        }

        public bool RemoveAnimated(ItemBase item)
        {
            int index = items.IndexOf(item);
            if (index >= 0) return RemoveAnimated(index, item); else return false;
        }

        private ItemAnimation GetAnimation(int index)
        {
            foreach (ItemAnimation a in itemAnimations)
            {
                if (a.Index == index) return a;
            }
            return null;
        }

        private bool RemoveAnimated(int index, ItemBase item)
        {
            ItemAnimation a = GetAnimation(index);
            if (a != null)
            {
                InsertItemAnimation insertAnim = a as InsertItemAnimation;
                if (insertAnim != null)
                {
                    a.Stop();
                    return false;
                }
            }

            a = new RemoveItemAnimation(this, index);
            itemAnimations.Add(a);
            a.Invoke();
            return true;
        }

        public bool RemoveAnimated(int index)
        {
            ItemBase item = items[index];
            return RemoveAnimated(index, item);
        }

        public void AddAnimated(ItemBase item)
        {
            InsertAnimated(items.Count, item);
        }

        public bool InsertAnimated(int index, ItemBase item)
        {
            items.Insert(index, item);

            ItemAnimation a = new InsertItemAnimation(this, index);
            itemAnimations.Add(a);
            a.Invoke();
            return true;
        }

    }
}
